package ch.bfh.mgb.hamilton.simulation.behaviour;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

import ch.bfh.mgb.hamilton.entities.Robot;
import ch.bfh.mgb.hamilton.gui.Display;
import ch.bfh.mgb.hamilton.simulation.Simulation;
import ch.bfh.mgb.hamilton.simulation.SimulationParameters;
import ch.bfh.mgb.hamilton.statistics.Statistics;

public class FitnessPopulation extends Simulation {

	public FitnessPopulation(Display display, Statistics statistics) {
		super(display, statistics);
	}

	/**
	 * Method for choosing couples of the current population in order to create
	 * new children
	 * 
	 * @param robots
	 *            the current population of robots
	 * @return ArrayList<Robot> the children
	 */
	@Override
	public List<Robot> chooseCouples(List<Robot> robots) {
		List<Robot> robotNewGeneration = new ArrayList<Robot>();
		List<Robot> potentialCouples = new ArrayList<Robot>(robots);
		List<Robot> couples = new ArrayList<Robot>();
		List<Robot> helpers = new ArrayList<Robot>();
		// Create a temporal deep copy of robots in order to choose them and
		// remove them
		// because in one round a robot can only be in one couple
		Collections.copy(potentialCouples, robots);

		Random rand = new Random();
		while (potentialCouples.size() != 0) {
			// For now, search really randomly two robots from the list
			int robot1ID = rand.nextInt(potentialCouples.size());
			int robot2ID = robot1ID;
			if (potentialCouples.size() > 1) {
				while (robot2ID == robot1ID) {
					robot2ID = rand.nextInt(potentialCouples.size());
				}

				// main part of the reproduction.
				// the better foodSearchers (fittest), the better
				if (robots.get(robot1ID).getFoodCount() > 0.0 
							&& robots.get(robot2ID).getFoodCount() > 0.0) {

					List<Robot> childs = createChildren(robots.get(robot1ID),
							robots.get(robot2ID));
					robotNewGeneration.addAll(childs);
					couples.add(potentialCouples.get(robot1ID));
					couples.add(potentialCouples.get(robot2ID));
				} else {
					helpers.add(potentialCouples.get(robot1ID));
					helpers.add(potentialCouples.get(robot2ID));
				}
				potentialCouples.remove((robot1ID < robot2ID) ? robot2ID
						: robot1ID);
				potentialCouples.remove((robot1ID < robot2ID) ? robot1ID
						: robot2ID);
			} else {
				helpers.add(potentialCouples.get(0));
				potentialCouples.remove(0);
			}
		}

		return robotNewGeneration;
	}

	public List<Robot> createChildren(Robot robot1, Robot robot2) {
		Random rand = new Random();
		// if(rand.nextDouble()<robot1.getreproductionFrequency() &&
		// rand.nextDouble()<robot2.getreproductionFrequency()){
		List<Robot> robotChildren = new ArrayList<Robot>();
		int childNumber = (int) ((robot1.getFertility() + robot2.getFertility()) / 2);// ((robot1.getFertility()+robot2.getFertility())*rand.nextGaussian()/2);
		for (int i = 1; i <= childNumber; i++) {
			boolean sex = true;
			double reproductionFrequency = (robot1.getReproductionFrequency() + robot2
					.getReproductionFrequency())
					/ 2
					+ SimulationParameters.mutationRateReproductionFrequency
					* rand.nextGaussian();
			int fertility = (int) ((robot1.getFertility() + robot2
					.getFertility()) / 2); // +mutationRateFertility*rand.nextGaussian());
			int timeToLive = rand.nextInt(SimulationParameters.maxTimeToLive);
			robotChildren.add(new Robot(sex, reproductionFrequency, fertility,
					setCurrentRobotID(), timeToLive, processNewFoodBehaviour(robot1, robot2)));
		}
		return robotChildren;
	}
	
	private int[] processNewFoodBehaviour(Robot r1, Robot r2) {
		int[] beh = new int[SimulationParameters.foodIterations];
		int genesFromR1 = new Double(((double)SimulationParameters.foodIterations) 
			/ ((r1.getFoodCount() + 1.0) + (r2.getFoodCount() + 1.0)) 
			* (r1.getFoodCount() + 1.0)).intValue();

		System.arraycopy(Arrays.copyOfRange(r1.getFoodBehaviour(), 0, genesFromR1)
				, 0, beh, 0, genesFromR1);
		System.arraycopy(Arrays.copyOfRange(r2.getFoodBehaviour(), genesFromR1, SimulationParameters.foodIterations)
				, 0, beh, genesFromR1, SimulationParameters.foodIterations - genesFromR1);
		
		List<Integer> lst = new ArrayList<Integer>(beh.length);
		for(int i : beh) {
			lst.add(i);
		}
		Collections.shuffle(lst);
		for(int i = 0; i < lst.size(); i++) {
			beh[i] = lst.get(i);
		}
		
		return beh;
	}
	
	public List<Robot> chosenFoodSearchers(List<Robot> pop, int count) {
		List<Robot> chosen = new ArrayList<Robot>();
		Collections.sort(pop, new Comparator<Robot>() {
			public int compare(Robot r1, Robot r2) {
				return ((Double)r1.getFoodCount()).compareTo(r2.getFoodCount());
			}
		});
		if (count > pop.size()) {
			return pop;
		}
		for (; count > 0; count--) {
			chosen.add(pop.get(count - 1));
		}
		return chosen;
	}
}
